#!/usr/bin/env python3
"""
SchedulerAI Environment Configuration Validation Script

This script validates environment variables and configuration before container startup.
It ensures all required variables are set with valid values and provides helpful error messages.
"""

import os
import sys
import re
from pathlib import Path
from typing import Dict, Any, List, Tuple, Optional


class EnvironmentValidator:
    """Environment configuration validator"""
    
    def __init__(self):
        self.errors: List[str] = []
        self.warnings: List[str] = []
        self.config: Dict[str, Any] = {}
    
    def validate_required_string(self, var_name: str, description: str, 
                                default: Optional[str] = None) -> str:
        """Validate a required string environment variable"""
        value = os.getenv(var_name, default)
        if not value:
            self.errors.append(f"Required environment variable {var_name} is not set ({description})")
            return ""
        
        self.config[var_name] = value
        return value
    
    def validate_integer(self, var_name: str, description: str, 
                        min_val: int = None, max_val: int = None, 
                        default: int = None) -> int:
        """Validate an integer environment variable"""
        value_str = os.getenv(var_name, str(default) if default is not None else None)
        if not value_str:
            if default is not None:
                self.config[var_name] = default
                return default
            else:
                self.errors.append(f"Required environment variable {var_name} is not set ({description})")
                return 0
        
        try:
            value = int(value_str)
            if min_val is not None and value < min_val:
                self.errors.append(f"{var_name} must be >= {min_val}, got {value}")
                return 0
            if max_val is not None and value > max_val:
                self.errors.append(f"{var_name} must be <= {max_val}, got {value}")
                return 0
            
            self.config[var_name] = value
            return value
        except ValueError:
            self.errors.append(f"{var_name} must be an integer, got '{value_str}'")
            return 0
    
    def validate_float(self, var_name: str, description: str, 
                      min_val: float = None, max_val: float = None, 
                      default: float = None) -> float:
        """Validate a float environment variable"""
        value_str = os.getenv(var_name, str(default) if default is not None else None)
        if not value_str:
            if default is not None:
                self.config[var_name] = default
                return default
            else:
                self.errors.append(f"Required environment variable {var_name} is not set ({description})")
                return 0.0
        
        try:
            value = float(value_str)
            if min_val is not None and value < min_val:
                self.errors.append(f"{var_name} must be >= {min_val}, got {value}")
                return 0.0
            if max_val is not None and value > max_val:
                self.errors.append(f"{var_name} must be <= {max_val}, got {value}")
                return 0.0
            
            self.config[var_name] = value
            return value
        except ValueError:
            self.errors.append(f"{var_name} must be a number, got '{value_str}'")
            return 0.0
    
    def validate_choice(self, var_name: str, description: str, 
                       choices: List[str], default: str = None) -> str:
        """Validate an environment variable that must be one of specific choices"""
        value = os.getenv(var_name, default)
        if not value:
            if default is not None:
                self.config[var_name] = default
                return default
            else:
                self.errors.append(f"Required environment variable {var_name} is not set ({description})")
                return ""
        
        if value not in choices:
            self.errors.append(f"{var_name} must be one of {choices}, got '{value}'")
            return ""
        
        self.config[var_name] = value
        return value
    
    def validate_memory_size(self, var_name: str, description: str, 
                           default: str = None) -> str:
        """Validate memory size format (e.g., 1g, 512m, 2048k)"""
        value = os.getenv(var_name, default)
        if not value:
            if default is not None:
                self.config[var_name] = default
                return default
            else:
                self.errors.append(f"Required environment variable {var_name} is not set ({description})")
                return ""
        
        # Validate memory size format
        if not re.match(r'^\d+[kmgKMG]?$', value):
            self.errors.append(f"{var_name} must be in format like '1g', '512m', '2048k', got '{value}'")
            return ""
        
        self.config[var_name] = value
        return value
    
    def validate_path(self, var_name: str, description: str, 
                     must_exist: bool = False, create_if_missing: bool = False,
                     default: str = None) -> str:
        """Validate a file system path"""
        value = os.getenv(var_name, default)
        if not value:
            if default is not None:
                self.config[var_name] = default
                value = default
            else:
                self.errors.append(f"Required environment variable {var_name} is not set ({description})")
                return ""
        
        path = Path(value)
        
        if must_exist and not path.exists():
            self.errors.append(f"Path {value} does not exist ({var_name})")
            return ""
        
        if create_if_missing and not path.exists():
            try:
                path.mkdir(parents=True, exist_ok=True)
                print(f"Created directory: {value}")
            except Exception as e:
                self.errors.append(f"Failed to create directory {value}: {e}")
                return ""
        
        self.config[var_name] = value
        return value
    
    def validate_boolean(self, var_name: str, description: str, 
                        default: bool = None) -> bool:
        """Validate a boolean environment variable"""
        value_str = os.getenv(var_name, str(default).lower() if default is not None else None)
        if not value_str:
            if default is not None:
                self.config[var_name] = default
                return default
            else:
                self.errors.append(f"Required environment variable {var_name} is not set ({description})")
                return False
        
        true_values = {'true', '1', 'yes', 'on', 'enabled'}
        false_values = {'false', '0', 'no', 'off', 'disabled'}
        
        value_lower = value_str.lower()
        if value_lower in true_values:
            self.config[var_name] = True
            return True
        elif value_lower in false_values:
            self.config[var_name] = False
            return False
        else:
            self.errors.append(f"{var_name} must be true/false, got '{value_str}'")
            return False


def validate_api_configuration(validator: EnvironmentValidator) -> None:
    """Validate API service configuration"""
    print("Validating API configuration...")
    
    # API Host
    api_host = validator.validate_required_string(
        "API_HOST", "API server bind address", "0.0.0.0"
    )
    
    # API Port
    api_port = validator.validate_integer(
        "API_PORT", "API server port", 1024, 65535, 8000
    )
    
    # API Workers
    api_workers = validator.validate_integer(
        "API_WORKERS", "Number of Gunicorn workers", 1, 32, 4
    )
    
    # Validate port is not in use (if possible)
    if api_port > 0:
        try:
            import socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(1)
            result = sock.connect_ex((api_host, api_port))
            sock.close()
            if result == 0:
                validator.warnings.append(f"Port {api_port} appears to be in use")
        except Exception:
            pass  # Ignore socket check errors


def validate_performance_configuration(validator: EnvironmentValidator) -> None:
    """Validate performance configuration"""
    print("Validating performance configuration...")
    
    # File size limits
    validator.validate_integer(
        "MAX_FILE_SIZE", "Maximum file upload size in bytes", 
        1024, 100*1024*1024, 10*1024*1024  # 1KB to 100MB, default 10MB
    )
    
    # Solver time limit
    validator.validate_integer(
        "SOLVER_TIME_LIMIT", "CP-SAT solver time limit in seconds",
        10, 3600, 300  # 10 seconds to 1 hour, default 5 minutes
    )
    
    # Request limits
    validator.validate_integer(
        "MAX_REQUESTS", "Maximum requests per worker", 100, 10000, 1000
    )
    
    validator.validate_integer(
        "TIMEOUT", "Request timeout in seconds", 30, 1800, 300
    )
    
    validator.validate_integer(
        "WORKER_CONNECTIONS", "Worker connection limit", 100, 10000, 1000
    )


def validate_resource_limits(validator: EnvironmentValidator) -> None:
    """Validate Docker resource limits"""
    print("Validating resource limits...")
    
    # Memory limits
    validator.validate_memory_size(
        "MEMORY_LIMIT", "Container memory limit", "2g"
    )
    
    validator.validate_memory_size(
        "MEMORY_RESERVATION", "Container memory reservation", "1g"
    )
    
    # CPU limits
    validator.validate_float(
        "CPU_LIMIT", "Container CPU limit", 0.1, 32.0, 2.0
    )
    
    validator.validate_float(
        "CPU_RESERVATION", "Container CPU reservation", 0.1, 32.0, 1.0
    )


def validate_logging_configuration(validator: EnvironmentValidator) -> None:
    """Validate logging configuration"""
    print("Validating logging configuration...")
    
    # Log level
    validator.validate_choice(
        "LOG_LEVEL", "Logging level",
        ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], "INFO"
    )
    
    # Log format
    validator.validate_choice(
        "LOG_FORMAT", "Log output format",
        ["json", "text"], "json"
    )
    
    # Log retention
    log_retention = validator.validate_required_string(
        "LOG_RETENTION", "Log retention period", "30d"
    )
    
    # Validate retention format
    if log_retention and not re.match(r'^\d+[dwmy]$', log_retention):
        validator.errors.append("LOG_RETENTION must be in format like '30d', '4w', '12m', '1y'")


def validate_security_configuration(validator: EnvironmentValidator) -> None:
    """Validate security configuration"""
    print("Validating security configuration...")
    
    # Allowed file extensions
    allowed_extensions = validator.validate_required_string(
        "ALLOWED_EXTENSIONS", "Allowed file extensions", "csv"
    )
    
    # Validate extensions format
    if allowed_extensions:
        extensions = [ext.strip() for ext in allowed_extensions.split(',')]
        for ext in extensions:
            if not re.match(r'^[a-zA-Z0-9]+$', ext):
                validator.errors.append(f"Invalid file extension '{ext}' in ALLOWED_EXTENSIONS")
    
    # CORS configuration
    validator.validate_boolean(
        "ENABLE_CORS", "Enable CORS support", True
    )
    
    cors_origins = validator.validate_required_string(
        "CORS_ORIGINS", "CORS allowed origins", 
        "http://localhost:3000,http://localhost:5173"
    )


def validate_storage_configuration(validator: EnvironmentValidator) -> None:
    """Validate storage configuration"""
    print("Validating storage configuration...")
    
    # Data volume paths
    validator.validate_path(
        "DATA_VOLUME_PATH", "Data volume mount path", 
        create_if_missing=True, default="./data"
    )
    
    validator.validate_path(
        "LOGS_VOLUME_PATH", "Logs volume mount path",
        create_if_missing=True, default="./logs"
    )
    
    validator.validate_path(
        "UPLOADS_VOLUME_PATH", "Uploads volume mount path",
        create_if_missing=True, default="./uploads"
    )


def validate_health_check_configuration(validator: EnvironmentValidator) -> None:
    """Validate health check configuration"""
    print("Validating health check configuration...")
    
    # Health check intervals
    health_interval = validator.validate_required_string(
        "HEALTH_CHECK_INTERVAL", "Health check interval", "30s"
    )
    
    health_timeout = validator.validate_required_string(
        "HEALTH_CHECK_TIMEOUT", "Health check timeout", "10s"
    )
    
    health_start_period = validator.validate_required_string(
        "HEALTH_CHECK_START_PERIOD", "Health check start period", "60s"
    )
    
    # Validate time format
    time_vars = [
        (health_interval, "HEALTH_CHECK_INTERVAL"),
        (health_timeout, "HEALTH_CHECK_TIMEOUT"),
        (health_start_period, "HEALTH_CHECK_START_PERIOD")
    ]
    
    for time_val, var_name in time_vars:
        if time_val and not re.match(r'^\d+[smh]$', time_val):
            validator.errors.append(f"{var_name} must be in format like '30s', '5m', '1h'")
    
    validator.validate_integer(
        "HEALTH_CHECK_RETRIES", "Health check retry count", 1, 10, 3
    )


def validate_development_configuration(validator: EnvironmentValidator) -> None:
    """Validate development configuration"""
    print("Validating development configuration...")
    
    validator.validate_boolean(
        "DEVELOPMENT_MODE", "Development mode", False
    )
    
    validator.validate_boolean(
        "ENABLE_DOCS", "Enable API documentation", True
    )
    
    validator.validate_boolean(
        "DETAILED_ERRORS", "Enable detailed error responses", False
    )


def main() -> bool:
    """Main validation function"""
    print("SchedulerAI Environment Configuration Validation")
    print("=" * 50)
    
    validator = EnvironmentValidator()
    
    # Run all validation checks
    validation_functions = [
        validate_api_configuration,
        validate_performance_configuration,
        validate_resource_limits,
        validate_logging_configuration,
        validate_security_configuration,
        validate_storage_configuration,
        validate_health_check_configuration,
        validate_development_configuration
    ]
    
    for validation_func in validation_functions:
        try:
            validation_func(validator)
        except Exception as e:
            validator.errors.append(f"Validation error in {validation_func.__name__}: {e}")
    
    # Print results
    print(f"\n=== Validation Results ===")
    
    if validator.warnings:
        print(f"\nWarnings ({len(validator.warnings)}):")
        for warning in validator.warnings:
            print(f"⚠ {warning}")
    
    if validator.errors:
        print(f"\nErrors ({len(validator.errors)}):")
        for error in validator.errors:
            print(f"✗ {error}")
        
        print(f"\n✗ Environment validation failed with {len(validator.errors)} errors")
        print("Please fix the configuration issues above before starting the container.")
        return False
    else:
        print(f"\n✓ Environment validation passed!")
        print(f"Configuration summary:")
        for key, value in sorted(validator.config.items()):
            if 'PASSWORD' in key or 'SECRET' in key or 'TOKEN' in key:
                print(f"  {key}: [HIDDEN]")
            else:
                print(f"  {key}: {value}")
        
        return True


if __name__ == "__main__":
    success = main()
    sys.exit(0 if success else 1)